package com.liang.lingdong.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.extra.mail.MailUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liang.lingdong.common.ErrorCode;
import com.liang.lingdong.constant.CommonConstant;
import com.liang.lingdong.constant.UserConstant;
import com.liang.lingdong.enums.UserStatusEnum;
import com.liang.lingdong.exception.BusinessException;
import com.liang.lingdong.model.user.User;
import com.liang.lingdong.mapper.UserMapper;
import com.liang.lingdong.model.user.dto.ForgetPasswordDto;
import com.liang.lingdong.model.user.dto.QueryUserListDto;
import com.liang.lingdong.model.user.dto.UpdatePasswordDto;
import com.liang.lingdong.model.user.vo.UserVo;
import com.liang.lingdong.service.UserService;
import com.liang.lingdong.utils.PasswordGenerator;
import com.liang.lingdong.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
* @author liang
* @description 针对表【user(用户表)】的数据库操作Service实现
* @createDate 2024-11-09 18:35:19
*/
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
    implements UserService {

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 分页查询用户
     * @param queryUserListDto 查询参数
     * @return 结果
     */
    @Override
    public IPage<UserVo> queryUserPage(QueryUserListDto queryUserListDto) {
        String username = queryUserListDto.getUsername();
        String userAccount = queryUserListDto.getUserAccount();
        Integer status = queryUserListDto.getStatus();
        Integer userRole = queryUserListDto.getUserRole();
        String className = queryUserListDto.getClassName();
        long current = queryUserListDto.getCurrent();
        long pageSize = queryUserListDto.getPageSize();
        // 构造查询条件
        Page<User> userPage = lambdaQuery()
                .like(StringUtils.isNotBlank(username), User::getUsername, username)
                .like(StringUtils.isNotBlank(userAccount), User::getUserAccount, userAccount)
                .eq(status != null, User::getStatus, status)
                .eq(userRole != null, User::getUserRole, userRole)
                .like(StringUtils.isNotBlank(className), User::getClassName, className)
                .page(new Page<>(current, pageSize));
        // 用户脱敏
        Page<UserVo> userVoPage = new Page<>();
        List<User> records = userPage.getRecords();
        // 拷贝属性
        BeanUtil.copyProperties(userPage, userVoPage);
        List<UserVo> collect = records.stream().map(user -> BeanUtil.toBean(user, UserVo.class)).collect(Collectors.toList());
        // 设置脱敏后的数据
        userVoPage.setRecords(collect);
        return userVoPage;
    }

    /**
     * 用户授权
     * @param businessId 用户id
     * @return 结果
     */
    @Override
    public boolean authUser(Long businessId) {
        // 查询用户
        User user = lambdaQuery()
                .eq(User::getBusinessId, businessId)
                .one();
        // 判断用户是否存在
        if (user == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在");
        }
        // 判断用户状态
        if (!UserStatusEnum.REVIEWING.getCode().equals(user.getStatus())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户无需审核");
        }
        // 随机生成10位数密码
        String password = PasswordGenerator.generatePassword();
        // 对密码进行加密
        String encryptPassword = DigestUtil.md5Hex(UserConstant.SALT + password);
        // 设置密码
        user.setUserPassword(encryptPassword);
        // 初始化邮箱内容
        String emailContent = "欢迎你加入灵动创意工坊！" + "\n" + "你于" + DateUtil.formatDateTime(user.getCreateTime())
                + "申请的账户已经通过审核。" + "\n"
                + "您的用户名是：" + user.getUserAccount() + "\n" + "您的初始化密码是：" + password + "\n"
                + "请您尽快进入系统修改密码，并严格保密本邮件内容，不要泄露给别人！";
        // 为用户发送初始化密码到邮箱
        sendEmail(user.getEmail(), emailContent);
        // 重置用户创建时间
        user.setCreateTime(new Date());
        // 更新用户信息
        user.setStatus(UserStatusEnum.NORMAL.getCode());
        // 更改用户状态
        boolean flag = this.updateById(user);
        if (!flag) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "用户授权失败");
        }
        return true;
    }

    /**
     * 查询单一用户信息
     * @param businessId 用户id
     * @return 结果
     */
    @Override
    public UserVo getUserInfo(Long businessId) {
        User user = lambdaQuery()
                .eq(User::getBusinessId, businessId)
                .one();
        if (user == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在");
        }
        return BeanUtil.toBean(user, UserVo.class);
    }

    /**
     * 用户修改密码
     * @param updatePasswordDto 修改密码参数
     * @return 结果
     */
    @Override
    public boolean updatePassword(UpdatePasswordDto updatePasswordDto) {
        Long businessId = updatePasswordDto.getBusinessId();
        String oldPassword = updatePasswordDto.getOldPassword();
        String newPassword = updatePasswordDto.getNewPassword();
        String confirmPassword = updatePasswordDto.getConfirmPassword();
        // 新旧密码不能相同
        if (oldPassword.equals(newPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "新旧密码不能相同");
        }
        if (!newPassword.equals(confirmPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次密码不一致");
        }
        User user = lambdaQuery()
                .eq(User::getBusinessId, businessId)
                .one();
        if (user == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在");
        }
        String encryptPassword = DigestUtil.md5Hex(UserConstant.SALT + oldPassword);
        if (!encryptPassword.equals(user.getUserPassword())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "旧密码不正确");
        }
        user.setUserPassword(DigestUtil.md5Hex(UserConstant.SALT + newPassword));
        boolean flag = this.updateById(user);
        if (!flag) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "密码修改失败");
        }
        return true;
    }

    /**
     * 用户忘记密码
     * @param forgetPasswordDto 忘记密码参数
     * @return 结果
     */
    @Override
    public boolean forgetPassword(ForgetPasswordDto forgetPasswordDto) {
        String email = forgetPasswordDto.getEmail();
        String authCode = forgetPasswordDto.getAuthCode();
        String codeId = forgetPasswordDto.getCodeId();
        String newPassword = forgetPasswordDto.getNewPassword();
        String confirmPassword = forgetPasswordDto.getConfirmPassword();
        if (!newPassword.equals(confirmPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次密码不一致");
        }
        // 校验验证码
        checkAuthCode(codeId, authCode, CommonConstant.EMAIL_CODE_REDIS_PREFIX);
        // 校验邮箱是否存在
        List<User> userList = lambdaQuery()
                .eq(User::getEmail, email)
                .ne(User::getStatus, UserStatusEnum.BAN.getCode())
                .list();
        if (userList.size() != 1) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"无效的邮箱");
        }
        User user = userList.get(0);
        String encryptPassword = DigestUtil.md5Hex(UserConstant.SALT + newPassword);
        user.setUserPassword(encryptPassword);
        boolean flag = this.updateById(user);
        if (!flag) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "密码修改失败");
        }
        destroyAuthCode(codeId, CommonConstant.EMAIL_CODE_REDIS_PREFIX);
        return true;

    }

    /**
     * 校验验证码
     *
     * @param uid    验证码id
     * @param answer 答案
     */
    private void checkAuthCode(String uid, String answer, String key) {
        if (StringUtils.isAnyBlank(uid, answer)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"请输入验证码");
        }
        // 构建redis key
        String redisKey = RedisUtils.buildKey(key, uid);
        if (Boolean.FALSE.equals(redisTemplate.hasKey(redisKey))) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"验证码已过期");
        }
        // 判断答案是否正确
        String redisAnswerCode = redisTemplate.opsForValue().get(redisKey);
        if (StringUtils.isAnyBlank(redisAnswerCode)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"验证码已失效");
        }
        // 统一转换大小写
        answer = answer.toLowerCase();
        redisAnswerCode = redisAnswerCode.toLowerCase();
        if (!answer.equals(redisAnswerCode)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"验证码不正确");
        }
    }

    /**
     * 发送邮件
     * @param email 目标邮箱
     * @param content 内容
     */
    private void sendEmail(String email, String content) {
        // 发送邮件
        try {
            MailUtil.send(email, "灵动创意工坊", content, false);
        } catch (Exception e) {
            log.error("邮件发送失败, 邮箱: {}, 错误: {}",email, e.getMessage());
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"邮件发送失败, 请稍后重试");
        }
    }

    /**
     * 销毁验证码
     * @param uid 验证码id
     * @return 结果
     */
    public boolean destroyAuthCode(String uid, String key) {
        String redisKey = RedisUtils.buildKey(CommonConstant.AUTH_CODE_REDIS_PREFIX, uid);
        return Boolean.TRUE.equals(redisTemplate.delete(redisKey));
    }
}




